package info.batcloud.fanli.core.service.impl;

import com.ctospace.archit.common.pagination.Paging;
import info.batcloud.fanli.core.dto.FlashSaleOrderDTO;
import info.batcloud.fanli.core.entity.CommissionItem;
import info.batcloud.fanli.core.entity.CommissionOrder;
import info.batcloud.fanli.core.entity.FlashSaleOrder;
import info.batcloud.fanli.core.enums.FlashSaleOrderStatus;
import info.batcloud.fanli.core.enums.WalletFlowDetailType;
import info.batcloud.fanli.core.event.CommissionOrderChangeEvent;
import info.batcloud.fanli.core.exception.BizException;
import info.batcloud.fanli.core.helper.PagingHelper;
import info.batcloud.fanli.core.repository.*;
import info.batcloud.fanli.core.service.FlashSaleOrderService;
import info.batcloud.fanli.core.service.WalletService;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.context.ApplicationListener;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.inject.Inject;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Service
public class FlashSaleOrderServiceImpl implements FlashSaleOrderService,
        ApplicationListener<CommissionOrderChangeEvent> {

    private static final Logger logger = LoggerFactory.getLogger(FlashSaleOrderService.class);

    @Inject
    private FlashSaleOrderRepository flashSaleItemOrderRepository;

    @Inject
    private CommissionItemRepository commissionItemRepository;

    @Inject
    private UserRepository userRepository;

    @Inject
    private FlashSaleItemRepository freeChargeActivityRepository;

    @Inject
    private CommissionOrderRepository commissionOrderRepository;

    @Inject
    private WalletService walletService;

    @Override
    public long createFlashSaleOrder(FlashSaleOrderCreateParam param) {
        FlashSaleOrder order = new FlashSaleOrder();
        order.setCreateTime(new Date());
        order.setStatus(FlashSaleOrderStatus.WAIT_VERIFY);
        order.setItem(commissionItemRepository.findOne(param.getItemId()));
        order.setUser(userRepository.findOne(param.getUserId()));
        order.setFlashSaleItem(freeChargeActivityRepository.findOne(param.getFlashSaleItemId()));
        order.setFreeFee(order.getFlashSaleItem().getReturnFee());
        flashSaleItemOrderRepository.save(order);
        return order.getId();
    }

    @Override
    public long countFlashSaleItemByUserIdAndFlashSaleItemId(long userId, long flashSaleItemId) {
        return flashSaleItemOrderRepository.countByUserIdAndFlashSaleItemId(userId, flashSaleItemId);
    }

    @Override
    public long countFlashSaleItemByUserIdAndFlashSaleItemIdAndCreateTimeBetween(long userId, long flashSaleItemId, Date startTime, Date endTime) {
        return flashSaleItemOrderRepository.countByUserIdAndFlashSaleItemIdAndCreateTimeBetween(userId, flashSaleItemId, startTime, endTime);
    }

    @Override
    public boolean checkHasWaitVerifyOrderByUserIdAndItemId(long userId, long itemId) {
        return flashSaleItemOrderRepository.countByUserIdAndItemIdAndStatus(userId, itemId, FlashSaleOrderStatus.WAIT_VERIFY) > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized void settle() {
        logger.info("开始结算免单活动订单");
        List<FlashSaleOrder> orders = flashSaleItemOrderRepository.findByStatus(FlashSaleOrderStatus.WAIT_SETTLE);
        for (FlashSaleOrder order : orders) {
            this.settleOrder(order);
        }
        logger.info("免单订单结算完成,共结算" + orders.size() + "个订单");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void settle(List<Long> ids) {
        logger.info("开始结算免单活动订单");
        Iterable<FlashSaleOrder> orders = flashSaleItemOrderRepository.findAll(ids);
        orders.forEach( order -> settleOrder(order));
        logger.info("免单订单结算完成,共结算" + ids.size() + "个订单");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteById(long id) {
        FlashSaleOrder order = flashSaleItemOrderRepository.findOne(id);
        if(order.getStatus() != FlashSaleOrderStatus.WAIT_VERIFY) {
            throw new BizException("只有待审核状态的才可以删除！");
        }
        flashSaleItemOrderRepository.delete(order);
    }

    private void settleOrder(FlashSaleOrder order) {
        if (order.getStatus() != FlashSaleOrderStatus.WAIT_SETTLE) {
            logger.error("该免单活动状态不是待结算状态，无法结算，id:" + order.getId());
            return;
        }
        order.setStatus(FlashSaleOrderStatus.SETTLED);
        order.setUpdateTime(new Date());
        walletService.addMoney(order.getUser().getId(), order.getFreeFee(),
                WalletFlowDetailType.FREE_CHARGE_ACTIVITY_REBATE, order.getId() + "");
        flashSaleItemOrderRepository.save(order);
    }

    @Override
    public Paging<FlashSaleOrderDTO> search(SearchParam param) {
        Specification<FlashSaleOrder> specification = (root, query, cb) -> {
            if(query.getResultType() != Long.class) {
                root.fetch("user", JoinType.LEFT);
                root.fetch("commissionOrder", JoinType.LEFT);
                root.fetch("flashSaleItem", JoinType.LEFT);
                root.fetch("item", JoinType.LEFT);
            }
            Predicate predicate = cb.conjunction();
            List<Expression<Boolean>> expressions = predicate.getExpressions();
            if (param.getStatus() != null) {
                expressions.add(cb.equal(root.get("status"), param.getStatus()));
            }
            if (StringUtils.isNotBlank(param.getDate())) {
                expressions.add(cb.equal(root.get("flashSaleItem").get("date"), param.getDate()));
            }
            if (StringUtils.isNotBlank(param.getSeason())) {
                expressions.add(cb.equal(root.get("flashSaleItem").get("season"), param.getSeason()));
            }
            if (param.getUserId() != null) {
                expressions.add(cb.equal(root.get("user").get("id"), param.getUserId()));
            }
            if (param.getItemId() != null) {
                expressions.add(cb.equal(root.get("item").get("id"), param.getItemId()));
            }
            if (StringUtils.isNotEmpty(param.getOrderNo())) {
                expressions.add(cb.equal(root.get("commissionOrder").get("orderNo"), param.getOrderNo()));
            }
            if (param.getFlashSaleItemId() != null) {
                expressions.add(cb.equal(root.get("flashSaleItem").get("id"), param.getFlashSaleItemId()));
            }
            if (param.getMinCreateTime() != null) {
                expressions.add(cb.greaterThanOrEqualTo(root.get("createTime"), param.getMinCreateTime()));
            }
            if (param.getMaxCreateTime() != null) {
                expressions.add(cb.lessThanOrEqualTo(root.get("createTime"), param.getMaxCreateTime()));
            }
            return predicate;
        };
        Sort sort = new Sort(Sort.Direction.DESC, "id");
        Pageable pageable = new PageRequest(param.getPage() - 1,
                param.getPageSize(), sort);
        Page<FlashSaleOrder> page = flashSaleItemOrderRepository.findAll(specification, pageable);
        return PagingHelper.of(toDTOList(page.getContent()), page.getTotalElements(), param.getPage(), param.getPageSize());
    }

    private List<FlashSaleOrderDTO> toDTOList(List<FlashSaleOrder> list) {
        List<FlashSaleOrderDTO> dtoList = new ArrayList<>();
        for (FlashSaleOrder order : list) {
            FlashSaleOrderDTO dto = new FlashSaleOrderDTO();
            BeanUtils.copyProperties(order, dto);
            dto.setFlashSaleItemId(order.getFlashSaleItem().getId());
            dto.setFlashSaleItemDate(order.getFlashSaleItem().getDate());
            dto.setFlashSaleItemSeason(order.getFlashSaleItem().getSeason());
            dto.setUserId(order.getUser().getId());
            dto.setUserAvatarUrl(order.getUser().getAvatarUrl());
            dto.setUserNickname(order.getUser().getNickname());
            dto.setItemId(order.getItem().getId());
            dto.setItemPicUrl(order.getItem().getPicUrl());
            dto.setItemPrice(order.getItem().getPrice());
            dto.setItemTitle(order.getItem().getTitle());
            if(order.getCommissionOrder() != null) {
                dto.setOrderNo(order.getCommissionOrder().getOrderNo());
            }
            dto.setItemShopTitle(order.getItem().getShopTitle());
            dto.setFreeFee(order.getFreeFee());
            if (order.getUpdateTime() == null) {
                dto.setUpdateTime(dto.getCreateTime());
            } else {
                dto.setUpdateTime(dto.getUpdateTime());
            }
            dtoList.add(dto);
        }
        return dtoList;
    }

    @Override
    public void onApplicationEvent(CommissionOrderChangeEvent event) {
        Long commissionOrderId = (Long) event.getSource();
        FlashSaleOrder order = flashSaleItemOrderRepository.findByCommissionOrderId(commissionOrderId);
        CommissionOrder co = commissionOrderRepository.findOne(commissionOrderId);
        if (co.getCommissionItemId() == null
                || co.getUserId() == null) {
            return;
        }
        if (order == null) {
            CommissionItem ci = commissionItemRepository.findOne(co.getCommissionItemId());
            order = flashSaleItemOrderRepository.findByUserIdAndItemId(co.getUserId(), ci.getId());
        }
        if (order == null) {
            return;
        }
        switch (co.getStatus()) {
            case PAID:
                if (order.getStatus() == FlashSaleOrderStatus.WAIT_VERIFY) {
                    order.setStatus(FlashSaleOrderStatus.WAIT_RECEIVE_GOODS);
                    order.setCommissionOrder(co);
                }
                break;
            case WAIT_SETTLE:
                if (order.getStatus() == FlashSaleOrderStatus.WAIT_RECEIVE_GOODS) {
                    order.setStatus(FlashSaleOrderStatus.WAIT_SETTLE);
                }
                break;
        }
        flashSaleItemOrderRepository.save(order);

    }
}
